package com.pomelo.kill.service.impl;

import cn.hutool.core.lang.ObjectId;
import com.pomelo.kill.common.Constants;
import com.pomelo.kill.common.KillUtil;
import com.pomelo.kill.exception.KillException;
import com.pomelo.kill.model.Address;
import com.pomelo.kill.model.Goods;
import com.pomelo.kill.model.Order;
import com.pomelo.kill.rabbit.Subscriber;
import com.pomelo.kill.service.AddressService;
import com.pomelo.kill.service.GoodsService;
import com.pomelo.kill.service.OrderService;
import com.pomelo.kill.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class RedisServiceImpl implements RedisService {


    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private Subscriber subscriber;

    @Resource
    private GoodsService goodsService;

    @Resource
    private AddressService addressService;

    @Resource
    private OrderService orderService;

    /**
     * 获取所有商品信息
     */
    @Override
    public List<Goods> GetAllKillData() {
        String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
        // 先在redis中查询今天参与秒杀商品信息
        String pattern = "*" + Constants.REDIS_KILL_GOODS + date + "*"; // 模糊查询的key
        Set<String> keys = getKeys(pattern, Constants.REDIS_KILL_GOODS);
        List<Goods> list = new ArrayList<>();
        if (keys == null || keys.size() == 0) { // redis 中没有，从数据库查询 存到redis里面
            list = goodsService.selectAllKill(date);
            for (Goods goods : list) {
                Integer killStock = goods.getStock().getKillStock();
                long offset = KillUtil.getOffset(goods);
                for (int i = 0; i < killStock; i++) {
                    // key  kill:goods:20221205:1:1 存储的是商品对象
                    String key = pattern.replace("*", "") + ":" + goods.getId() + ":" + i;
                    redisTemplate.opsForValue().set(key, goods,offset, TimeUnit.MILLISECONDS);
                }
            }
        } else {
            for (String key : keys) {
                Goods goods = (Goods) redisTemplate.opsForValue().get(key);
                if (!list.contains(goods)) {
                    list.add(goods);
                }
            }
        }
        return list;
    }

    /**
     * 生成订单，从redis删除库存，加入队列
     *
     * @param uid 用户编号
     * @param gid 商品编号
     * @return 订单信息
     */
    @Override
    public Order order(Integer uid, Integer gid, Integer state) throws KillException {
        Order order = orderService.selectByUidAndGid(uid, gid);
        if (order != null) { // 已经存在付款订单
            throw new KillException("每个用户限购一件");
        }
        Map<String, Order> temp = (Map<String, Order>) redisTemplate.opsForValue().get(Constants.REDIS_KILL_ORDER_USER + uid + ":" + gid + ":0");
        if (temp != null) { // 存在未付款订单
            throw new KillException("存在待支付的订单");
        }
        order = new Order();
        order.setId(ObjectId.next());
        order.setGid(gid);
        order.setGoods(goodsService.selectByPrimaryKey(gid));
        order.setUid(uid);
        // 获取当前用户的所有地址
        List<Address> list = addressService.selectByUid(uid);
        if (list.size() != 0) {
            order.setAddressId(list.get(0).getId()); // 默认设置成第一个地址
            for (Address address : list) {
                if (address.getDefaultAdd()) { // 如果有默认地址设置成默认地址
                    order.setAddressId(address.getId());
                }
            }
            order.setAddress(addressService.selectByPrimaryKey(order.getAddressId()));
        }
        order.setState(state == null ? 0 : state);
        order.setCreateDate(new Date());
        String remove = remove(order);// 从redis删除一条记录
        if (remove == null) {
            log.error("redis删除失败");
            return null;
        }
        if (state == null || state == 0) { // 订单未支付，加入redis中
            Map<String, Order> map = new HashMap<>();
            map.put(remove, order);
            // key 值是 kill:order:order.id 有效期 15分钟
            redisTemplate.opsForValue().set(Constants.REDIS_KILL_ORDER + order.getId(), order, 15, TimeUnit.MINUTES);
            // key值是 kill:order:user:1:0 有效期 15分钟 存储的是Map对象
            redisTemplate.opsForValue().set(Constants.REDIS_KILL_ORDER_USER + uid + ":" + gid + ":0", map, 15, TimeUnit.MINUTES);
            log.info("订单加入redis");
        } else { // 订单已支付，加入队列
            order.setPayMoney(goodsService.selectByPrimaryKey(gid).getKillPrice());
            order.setPayTime(new Date());
            log.info("订单加入队列");
            subscriber.send(Constants.SECONDS_KILL_ORDER_QUEUE, "订单支付成功", order);
        }
        return order;
    }

    /**
     * 根据用户id查询在redis中的未支付订单信息
     *
     * @param uid 用户id
     * @return 未支付订单信息
     */
    @Override
    public List<Order> obligation(Integer uid) {
        String pattern = "*" + Constants.REDIS_KILL_ORDER_USER + uid + "*" + ":0";
        Set<String> keys = getKeys(pattern, Constants.REDIS_KILL_ORDER_USER);
        List<Order> orders = new ArrayList<>();
        for (String key : keys) {
            Map<String, Order> map = (Map<String, Order>) redisTemplate.opsForValue().get(key);
            assert map != null;
            orders.addAll(map.values());
        }
        return orders;
    }

    /**
     * 通过用户编号和商品编号查看待付款订单
     *
     * @param uid 用户编号
     * @param gid 商品编号
     * @return 待付款订单信息
     */
    @Override
    public Order obligationByGid(Integer uid, Integer gid) {
        Map<String, Order> map = (Map<String, Order>) redisTemplate.opsForValue().get(Constants.REDIS_KILL_ORDER_USER + uid + ":" + gid + ":0");
        if (map == null) {
            return null;
        }
        List<Order> list = new ArrayList<>(map.values());
        return list.get(0);
    }

    /**
     * 支付未支付的订单，先从redis中查询未支付的订单，修改订单状态，加入MQ队列
     *
     * @param id 订单id
     * @return
     */
    @Override
    public Order pay(String id) throws KillException {
        Order order = (Order) redisTemplate.opsForValue().get(Constants.REDIS_KILL_ORDER + id);
        assert order != null;
        order.setState(1);
        order.setPayTime(new Date());
        order.setPayMoney(goodsService.selectByPrimaryKey(order.getGid()).getKillPrice());
        // 加入队列，执行删除库存以及加入订单表操作
        subscriber.send(Constants.SECONDS_KILL_ORDER_QUEUE, "付款成功", order);
        // 从redis中删除
        redisTemplate.delete(Constants.REDIS_KILL_ORDER + id);
        redisTemplate.delete(Constants.REDIS_KILL_ORDER_USER + order.getUid() + ":" + order.getGid() + ":0");
        return order;
    }

    /**
     * 未付款订单取消，从redis中删除，将库存还回redis秒杀商品中
     *
     * @param id 订单id
     * @return
     */
    @Override
    public int cancel(String id) {
        String key = Constants.REDIS_KILL_ORDER + id;
        Order order = (Order) redisTemplate.opsForValue().get(key);
        Boolean delete = redisTemplate.delete(key);
        if (Boolean.TRUE.equals(delete)) {
            assert order != null;
            key = Constants.REDIS_KILL_ORDER_USER + order.getUid() + ":" + order.getGid() + ":0";
            // 获取到存储在redis里的map对象，将商品返回到秒杀商品redis 中
            Map<String, Order> map = (Map<String, Order>) redisTemplate.opsForValue().get(key);
            assert map != null;
            Set<String> keySet = map.keySet();
            for (String k : keySet) {
                // 将失效的商品信息返回到 redis 商品秒杀列表中
                Goods goods = goodsService.selectByPrimaryKey(map.get(k).getGid());
                log.info("秒杀商品加入redis");
                redisTemplate.opsForValue().set(k, goods, KillUtil.getOffset(goods), TimeUnit.MICROSECONDS);
            }
            Boolean d = redisTemplate.delete(key);
            if (Boolean.TRUE.equals(d)) {
                return 1;
            }
        }
        return 0;
    }

    /**
     * 秒杀成功后从redis中删除一条记录
     *
     * @param record 秒杀的订单信息
     * @return 删除的redis key
     */
    private String remove(Order record) {
        Goods goods = goodsService.selectByPrimaryKey(record.getGid());
        String pattern = "*" + Constants.REDIS_KILL_GOODS + new SimpleDateFormat("yyyyMMdd").format(goods.getKillTime()) + ":" + goods.getId() + "*";
        Set<String> keys = getKeys(pattern, Constants.REDIS_KILL_GOODS);
        String key = (String) keys.toArray()[0];
        Boolean delete = redisTemplate.delete(key);
        return Boolean.TRUE.equals(delete) ? key : null;
    }

    /**
     * 获取包含 表达式 的redis的key的集合
     *
     * @param pattern 表达式
     * @return redis符合条件的key集合
     */
    private Set<String> getKeys(String pattern, String sep) {
        return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> binaryKeys = new HashSet<>();
            Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(pattern).count(Integer.MAX_VALUE).build());
            while (cursor.hasNext()) {
                String key = new String(cursor.next());
                binaryKeys.add(key.substring(key.indexOf(sep)));
            }
            return binaryKeys;
        });
    }
}
